/*
* This source file is part of the EtherCAT Slave Stack Code licensed by Beckhoff Automation GmbH & Co KG, 33415 Verl, Germany.
* The corresponding license agreement applies. This hint shall not be removed.
*/

/**
\addtogroup EL9800_HW EL9800 Platform (Serial ESC Access)
@{
*/

/**
\file    el9800hw.c
\author EthercatSSC@beckhoff.com
\brief Implementation
Hardware access implementation for EL9800 onboard PIC18/PIC24 connected via SPI to ESC

\version 5.12

<br>Changes to version V5.11:<br>
V5.12 EL9800 1: improve the SPI access<br>
<br>Changes to version V5.10:<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
V5.11 EL9800 2: change PDI access test to 32Bit ESC access and reset AL Event mask after test even if AL Event is not enabled<br>
<br>Changes to version V5.01:<br>
V5.10 ESC5: Add missing swapping<br>
V5.10 HW3: Sync1 Isr added<br>
V5.10 HW4: Add volatile directive for direct ESC DWORD/WORD/BYTE access<br>
           Add missing swapping in mcihw.c<br>
           Add "volatile" directive vor dummy variables in enable and disable SyncManger functions<br>
           Add missing swapping in EL9800hw files<br>
<br>Changes to version V5.0:<br>
V5.01 HW1: Invalid ESC access function was used<br>
<br>Changes to version V4.40:<br>
V5.0 ESC4: Save SM disable/Enable. Operation may be pending due to frame handling.<br>
<br>Changes to version V4.30:<br>
V4.40 : File renamed from spihw.c to el9800hw.c<br>
<br>Changes to version V4.20:<br>
V4.30 ESM: if mailbox Syncmanger is disabled and bMbxRunning is true the SyncManger settings need to be revalidate<br>
V4.30 EL9800: EL9800_x hardware initialization is moved to el9800.c<br>
V4.30 SYNC: change synchronisation control function. Add usage of 0x1C32:12 [SM missed counter].<br>
Calculate bus cycle time (0x1C32:02 ; 0x1C33:02) CalcSMCycleTime()<br>
V4.30 PDO: rename PDO specific functions (COE_xxMapping -> PDO_xxMapping and COE_Application -> ECAT_Application)<br>
V4.30 ESC: change requested address in GetInterruptRegister() to prevent acknowledge events.<br>
(e.g. reading an SM config register acknowledge SM change event)<br>
GENERIC: renamed several variables to identify used SPI if multiple interfaces are available<br>
V4.20 MBX 1: Add Mailbox queue support<br>
V4.20 SPI 1: include SPI RxBuffer dummy read<br>
V4.20 DC 1: Add Sync0 Handling<br>
V4.20 PIC24: Add EL9800_4 (PIC24) required source code<br>
V4.08 ECAT 3: The AlStatusCode is changed as parameter of the function AL_ControlInd<br>
<br>Changes to version V4.02:<br>
V4.03 SPI 1: In ISR_GetInterruptRegister the NOP-command should be used.<br>
<br>Changes to version V4.01:<br>
V4.02 SPI 1: In HW_OutputMapping the variable u16OldTimer shall not be set,<br>
             otherwise the watchdog might exceed too early.<br>
<br>Changes to version V4.00:<br>
V4.01 SPI 1: DI and DO were changed (DI is now an input for the uC, DO is now an output for the uC)<br>
V4.01 SPI 2: The SPI has to operate with Late-Sample = FALSE on the Eva-Board<br>
<br>Changes to version V3.20:<br>
V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to<br>
              the EtherCAT Guidelines and Protocol Enhancements Specification<br>
V4.00 APPL 1: The watchdog checking should be done by a microcontroller<br>
                 timer because the watchdog trigger of the ESC will be reset too<br>
                 if only a part of the sync manager data is written<br>
V4.00 APPL 4: The EEPROM access through the ESC is added

*/


/*--------------------------------------------------------------------------------------
------
------    Includes
------
--------------------------------------------------------------------------------------*/

#include "ecat_def.h"

#include "ecatslv.h"


#define    _USERHW_ 1
#include "UserHw.h"
#undef    _USERHW_
/*remove definition of _EL9800HW_ (#ifdef is used in el9800hw.h)*/

#include "ecatappl.h"

#if (ESC_IFC == ESC_SPI)
#if (TARGET_MCU == APM32_MCU)
#if SPI_OPTIMIZED 
UINT8	SpiDummyBuff[1350];
#endif
#endif
/*--------------------------------------------------------------------------------------
------
------    internal Types and Defines
------
--------------------------------------------------------------------------------------*/

typedef union
{
    unsigned short    Word;
    unsigned char    Byte[2];
} UBYTETOWORD;

typedef union 
{
    UINT8           Byte[2];
    UINT16          Word;
}
UALEVENT;

/*-----------------------------------------------------------------------------------------
------
------    SPI defines/macros
------
-----------------------------------------------------------------------------------------*/
#if (TARGET_MCU == APM32_MCU)
#define			SELECT_SPI                   GPIO_ResetBit(SPI_CS_GPIO_Port,SPI_CS_Pin);
#define			DESELECT_SPI                 GPIO_SetBit(SPI_CS_GPIO_Port,SPI_CS_Pin) ;
#define			SPI_DEACTIVE                    1
#define			SPI_ACTIVE                      0
#endif

/*-----------------------------------------------------------------------------------------
------
------    Global Interrupt setting
------
-----------------------------------------------------------------------------------------*/
#if (TARGET_MCU == APM32_MCU)
#define DISABLE_GLOBAL_INT           		   __disable_irq()			
#define ENABLE_GLOBAL_INT           		    __enable_irq()	
#endif
#define    DISABLE_AL_EVENT_INT        DISABLE_GLOBAL_INT
#define    ENABLE_AL_EVENT_INT            ENABLE_GLOBAL_INT


/*-----------------------------------------------------------------------------------------
------
------    ESC Interrupt
------
-----------------------------------------------------------------------------------------*/
#if (TARGET_MCU == APM32_MCU)
#define    INIT_ESC_INT
#define    EcatIsr                   		
#define    ACK_ESC_INT   
#endif


/*-----------------------------------------------------------------------------------------
------
------    SYNC0 Interrupt
------
-----------------------------------------------------------------------------------------*/
#if (TARGET_MCU == APM32_MCU)
#define    INIT_SYNC0_INT										
#define    Sync0Isr													EINT3_IRQHandler // primary interrupt vector name
#define    DISABLE_SYNC0_INT								NVIC_DisableIRQ(SYNC0_EXTI_IRQn);
#define    ENABLE_SYNC0_INT									NVIC_EnableIRQ(SYNC0_EXTI_IRQn);
#define    ACK_SYNC0_INT										

#define    INIT_SYNC1_INT										
#define    Sync1Isr                        	EINT2_IRQHandler
#define    DISABLE_SYNC1_INT                NVIC_DisableIRQ(SYNC1_EXTI_IRQn);
#define    ENABLE_SYNC1_INT                 NVIC_EnableIRQ(SYNC1_EXTI_IRQn);
#define    ACK_SYNC1_INT  
#endif

/*-----------------------------------------------------------------------------------------
------
------    Hardware timer
------
-----------------------------------------------------------------------------------------*/
#if (TARGET_MCU == APM32_MCU)
#define			INIT_ECAT_TIMER
#define			STOP_ECAT_TIMER            			NVIC_DisableIRQ(TIMx_IRQn);\
																						TMR_Disable(TMR2);
#define			START_ECAT_TIMER          	  	NVIC_EnableIRQ(TIMx_IRQn);\
																						TMR_Enable(TMR2);
#endif


/*-----------------------------------------------------------------------------------------
------
------    Configuration Bits
------
-----------------------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------------
------
------    LED defines
------
-----------------------------------------------------------------------------------------*/
// EtherCAT Status LEDs -> StateMachine
#if (TARGET_MCU == APM32_MCU)
#define LED_ECATGREEN               
#define LED_ECATRED 
#endif
/*--------------------------------------------------------------------------------------
------
------    internal Variables
------
--------------------------------------------------------------------------------------*/
UALEVENT         EscALEvent;            //contains the content of the ALEvent register (0x220), this variable is updated on each Access to the Esc

/*--------------------------------------------------------------------------------------
------
------    internal functions
------
--------------------------------------------------------------------------------------*/

/*ECATCHANGE_START(V5.12) EL9800 1*/
#if SPI_OPTIMIZED
#else
static UINT8 RxTxSpiData(UINT8 MosiByte)
{
#if (TARGET_MCU == APM32_MCU) 
		VARVOLATILE UINT8 MisoByte = 0;  
		while(!SPI_I2S_ReadStatusFlag(SPI1, SPI_FLAG_TXBE)){};
    SPI_I2S_TxData(SPI1, MosiByte);
		while(!SPI_I2S_ReadStatusFlag(SPI1, SPI_FLAG_RXBNE));
		MisoByte = SPI_I2S_RxData(SPI1);
		return MisoByte;
 #endif   
}
#endif
static void SetSpiDR2DMA()
{
#if (TARGET_MCU == APM32_MCU) 
	#if SPI_OPTIMIZED
    DMA1_Channel3->CHPADDR = (uint32_t)&(SPI1->DATA);
    DMA1_Channel2->CHPADDR = (uint32_t)&(SPI1->DATA);	
		SPI1->CTRL2_B.RXDEN=1;
		SPI1->CTRL2_B.TXDEN=1;
		memset(SpiDummyBuff,0x00,sizeof(SpiDummyBuff));//zzc cost too muchu time
	#endif
#endif	
}
#if SPI_OPTIMIZED
static void RxTxSpiData_nW(UINT8* pdata, UINT8 n)
{
#if (TARGET_MCU == APM32_MCU) 
		/* Configure DMA Channel data length */
	  DMA1_Channel3->CHNDATA = n;

    /* Configure DMA Channel source address */
	  //SET_BIT(DMA1_Channel3->CCR,(DMA_CCR_EN));
    DMA1_Channel3->CHMADDR = (uint32_t)pdata;
	
    /* Enable the Peripheral */
	  //SET_BIT(DMA1_Channel3->CCR,(DMA_CCR_EN));
	  DMA1_Channel3->CHCFG_B.CHEN = 1;
		
    /* Clear all flags */
		//SET_BIT(DMA1->IFCR,DMA_ISR_TCIF2);
		DMA1->INTFCLR_B.TCCLR2 = 1;
		
		while(DMA1->INTSTS_B.TCFLG3==0){;}
			//SET_BIT(DMA1->IFCR,DMA_ISR_TCIF3);
			DMA1->INTFCLR_B.TCCLR3 = 1;
			//CLEAR_BIT(DMA1_Channel3->CCR,(DMA_CCR_EN));
			DMA1_Channel3->CHCFG_B.CHEN = 1;
			
		while(DMA1->INTSTS_B.TCFLG2==0);
 #endif   
}
#endif
#if SPI_OPTIMIZED
static void RxTxSpiData_nRW(UINT8* pdataRx,UINT8* pdataTx, UINT16 n)
{
#if (TARGET_MCU == APM32_MCU) 
    /* Disable the peripheral */
	  //CLEAR_BIT(DMA1_Channel2->CCR,(DMA_CCR_EN));
		DMA1_Channel2->CHCFG_B.CHEN = 0;
	
		/* Configure DMA Channel data length */
	  DMA1_Channel2->CHNDATA = n;

    /* Configure DMA Channel destination address */
    //DMA1_Channel2->CMAR = (uint32_t)pdataRx;
		DMA1_Channel2->CHMADDR = (uint32_t)pdataRx;
		
    /* Enable the Peripheral */
	  //SET_BIT(DMA1_Channel2->CCR,(DMA_CCR_EN));
		DMA1_Channel2->CHCFG_B.CHEN = 1;
	
		/* Configure DMA Channel data length */
	  //DMA1_Channel3->CNDTR = n;
		DMA1_Channel3->CHNDATA = n;
	
    /* Configure DMA Channel source address */
    DMA1_Channel3->CHMADDR = (uint32_t)pdataTx;
	
    /* Clear all flags */
		//SET_BIT(DMA1->IFCR,DMA_ISR_TCIF2);
		DMA1->INTFCLR_B.TCCLR2= 1 ;
		
    /* Enable the Peripheral */
	  //SET_BIT(DMA1_Channel3->CCR,(DMA_CCR_EN));
		DMA1_Channel3->CHCFG_B.CHEN = 1;
		
		while(DMA1->INTSTS_B.TCFLG3 == 0){;}//Main Time of Data Write/Read, could be used
			
		//SET_BIT(DMA1->IFCR,DMA_ISR_TCIF3);
		DMA1->INTFCLR_B.TCCLR3 = 1;
		//CLEAR_BIT(DMA1_Channel3->CCR,(DMA_CCR_EN));
		DMA1_Channel3->CHCFG_B.CHEN = 0;
			
		while(DMA1->INTSTS_B.TCFLG2 == 0);
#endif
}
#endif
#if SPI_OPTIMIZED
static void RxTxSpiData_nR(UINT8* pdata,UINT8 n)
{
#if (TARGET_MCU == APM32_MCU) 
		 //CLEAR_BIT(DMA1_Channel2->CCR,(DMA_CCR_EN));
		DMA1_Channel2->CHCFG_B.CHEN = 0;
	
    /* Clear all flags */

		/* Configure DMA Channel data length */
	  DMA1_Channel2->CHNDATA = n;
	
    /* Configure DMA Channel source address */

    /* Configure DMA Channel destination address */
    DMA1_Channel2->CHMADDR = (uint32_t)pdata;
	
    /* Enable the Peripheral */
	 // SET_BIT(DMA1_Channel2->CCR,(DMA_CCR_EN));
		DMA1_Channel2->CHCFG_B.CHEN = 1;
 #endif   
}
#endif
/////////////////////////////////////////////////////////////////////////////////////////
/**
 \param Address     EtherCAT ASIC address ( upper limit is 0x1FFF )    for access.
 \param Command    ESC_WR performs a write access; ESC_RD performs a read access.

 \brief The function addresses the EtherCAT ASIC via SPI for a following SPI access.
*////////////////////////////////////////////////////////////////////////////////////////
#if SPI_OPTIMIZED
static void AddressingEsc_Data(UINT8*dataSend, UINT16 Address, UINT8 Command )
{
		UBYTETOWORD tmp;
	  unsigned char Byte2;
		if(Address<0x2000)
		{
			tmp.Word = ( Address << 3 ) | Command;
			dataSend[0]=	tmp.Byte[1];
			dataSend[1]=	tmp.Byte[0];			
			/* select the SPI */
			SELECT_SPI;
			if(Command==ESC_RD03)
			{
				dataSend[2]=0xFF;
			}


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
			//RxTxSpiData_nR(dummy,2);
			//RxTxSpiData_nW(dataSend,2);
			//RxTxSpiData_nRW(dummy,dataSend,2);
		
			/* send the second address/command byte to the ESC */
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */
		}
		else
		{
			tmp.Word = ( Address << 3 ) | ESC_RD | ESC_WR;
			Byte2 = ((Address&0xE000)>>8) | (Command << 2);
			dataSend[0]=	tmp.Byte[1];
			dataSend[1]=	tmp.Byte[0];	
			dataSend[2]=	Byte2;
			/* select the SPI */
			SELECT_SPI;


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
		
			/* send the second address/command byte to the ESC */
	 
			//RxTxSpiData_nR(dummy,3);
			//RxTxSpiData_nW(dataSend,3);
			//RxTxSpiData_nRW(dummy,dataSend,3);
			if(Command==ESC_RD03)
			{
				dataSend[3]=0xFF;
			}
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */		
		}
}
#endif
static void AddressingEsc( UINT16 Address, UINT8 Command )
{
#if  (TARGET_MCU == APM32_MCU)
	#if SPI_OPTIMIZED
	  UBYTETOWORD tmp;
	  unsigned char Byte2;
    UINT8 dummy[3];
		UINT8 dataSend[3];
		if(Address<0x2000)
		{
			tmp.Word = ( Address << 3 ) | Command;
			dataSend[0]=	tmp.Byte[1];
			dataSend[1]=	tmp.Byte[0];			
			/* select the SPI */
			SELECT_SPI;


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
			//RxTxSpiData_nR(dummy,2);
			//RxTxSpiData_nW(dataSend,2);
			RxTxSpiData_nRW(dummy,dataSend,2);
		
			/* send the second address/command byte to the ESC */
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */
		}
		else
		{
			tmp.Word = ( Address << 3 ) | ESC_RD | ESC_WR;
			Byte2 = ((Address&0xE000)>>8) | (Command << 2);
			dataSend[0]=	tmp.Byte[1];
			dataSend[1]=	tmp.Byte[0];	
			dataSend[2]=	Byte2;
			/* select the SPI */
			SELECT_SPI;


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
		
			/* send the second address/command byte to the ESC */
	 
			//RxTxSpiData_nR(dummy,3);
			//RxTxSpiData_nW(dataSend,3);
			RxTxSpiData_nRW(dummy,dataSend,3);
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */		
		}
	#else
    UBYTETOWORD tmp;
	  unsigned char Byte2;
    VARVOLATILE UINT8 dummy;
		if(Address<0x2000)
		{
			tmp.Word = ( Address << 3 ) | Command;
				
			/* select the SPI */
			SELECT_SPI;


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
			dummy = 	RxTxSpiData(tmp.Byte[1]);
		
			/* send the second address/command byte to the ESC */
	 
			dummy =	RxTxSpiData(tmp.Byte[0]);
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */
		}
		else
		{
			tmp.Word = ( Address << 3 ) | ESC_RD | ESC_WR;
			Byte2 = ((Address&0xE000)>>8) | (Command << 2);
			/* select the SPI */
			SELECT_SPI;


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
			dummy = 	RxTxSpiData(tmp.Byte[1]);
		
			/* send the second address/command byte to the ESC */
	 
			dummy =	RxTxSpiData(tmp.Byte[0]);
			
			dummy =	RxTxSpiData(Byte2);
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */		
		}
	#endif
#endif
}
#if !INTERRUPTS_SUPPORTED
#define ISR_AddressingEsc AddressingEsc
#else
static void ISR_AddressingEsc_Data(UINT8*dataSend, UINT16 Address, UINT8 Command )
{
		UBYTETOWORD tmp;
	  unsigned char Byte2;
		if(Address<0x2000)
		{
			tmp.Word = ( Address << 3 ) | Command;
			dataSend[0]=	tmp.Byte[1];
			dataSend[1]=	tmp.Byte[0];			
			/* select the SPI */
			SELECT_SPI;
			if(Command==ESC_RD03)
			{
				dataSend[2]=0xFF;
			}


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
			//RxTxSpiData_nR(dummy,2);
			//RxTxSpiData_nW(dataSend,2);
			//RxTxSpiData_nRW(dummy,dataSend,2);
		
			/* send the second address/command byte to the ESC */
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */
		}
		else
		{
			tmp.Word = ( Address << 3 ) | ESC_RD | ESC_WR;
			Byte2 = ((Address&0xE000)>>8) | (Command << 2);
			dataSend[0]=	tmp.Byte[1];
			dataSend[1]=	tmp.Byte[0];	
			dataSend[2]=	Byte2;
			/* select the SPI */
			SELECT_SPI;


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
		
			/* send the second address/command byte to the ESC */
	 
			//RxTxSpiData_nR(dummy,3);
			//RxTxSpiData_nW(dataSend,3);
			//RxTxSpiData_nRW(dummy,dataSend,3);
			if(Command==ESC_RD03)
			{
				dataSend[3]=0xFF;
			}
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */		
		}
}
static void ISR_AddressingEsc( UINT16 Address, UINT8 Command )
{
#if (TARGET_MCU == APM32_MCU)
	#if SPI_OPTIMIZED
    UBYTETOWORD tmp;
	  unsigned char Byte2;
    UINT8 dummy[4];
		UINT8 dataSend[4];
		if(Address<0x2000)
		{
			tmp.Word = ( Address << 3 ) | Command;
			dataSend[0]=	tmp.Byte[1];
			dataSend[1]=	tmp.Byte[0];	
			
    /* select the SPI */
    SELECT_SPI;

    /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
      the transmission shall be started */
	
    /* send the first address/command byte to the ESC */
		
		 /* send the second address/command byte to the ESC */
			
			//RxTxSpiData_nR(dummy,2);
			//RxTxSpiData_nW(dataSend,2);			
			if(Command==ESC_RD03)
			{
				dataSend[2]=0xFF;
				RxTxSpiData_nRW(dummy,dataSend,3);
			}
			else
			{
				RxTxSpiData_nRW(dummy,dataSend,2);
			}
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */
		}
		else
		{
			tmp.Word = ( Address << 3 ) | ESC_RD | ESC_WR;
			Byte2 = ((Address&0xE000)>>8) | (Command << 2);
			dataSend[0]=	tmp.Byte[1];
			dataSend[1]=	tmp.Byte[0];	
			dataSend[2]=	Byte2;
			/* select the SPI */
			SELECT_SPI;


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
			/* send the second address/command byte to the ESC */
			
			//RxTxSpiData_nR(dummy,3);
			//RxTxSpiData_nW(dataSend,3);	
			if(Command==ESC_RD03)
			{
				dataSend[3]=0xFF;
				RxTxSpiData_nRW(dummy,dataSend,4);
			}
			else
			{
				RxTxSpiData_nRW(dummy,dataSend,3);
			}
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */		
		}	
		#else
    VARVOLATILE UINT8 dummy;
    UBYTETOWORD tmp;
	  unsigned char Byte2;
	
		if(Address<0x2000)
		{
			tmp.Word = ( Address << 3 ) | Command;
			
    /* select the SPI */
    SELECT_SPI;

    /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
      the transmission shall be started */
	
    /* send the first address/command byte to the ESC */
		dummy= RxTxSpiData(tmp.Byte[1]);
		
		 /* send the second address/command byte to the ESC */

		dummy= RxTxSpiData(tmp.Byte[0]);
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */
		}
		else
		{
			tmp.Word = ( Address << 3 ) | ESC_RD | ESC_WR;
			Byte2 = ((Address&0xE000)>>8) | (Command << 2);
			/* select the SPI */
			SELECT_SPI;


		 /* there have to be at least 15 ns after the SPI1_SEL signal was active (0) before
				 the transmission shall be started */
			/* send the first address/command byte to the ESC */
		
			dummy = 	RxTxSpiData(tmp.Byte[1]);
		
			/* send the second address/command byte to the ESC */
	 
			dummy =	RxTxSpiData(tmp.Byte[0]);
			
			dummy =	RxTxSpiData(Byte2);
			
    /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */		
		}
		#endif
#endif
}
#endif //#if !INTERRUPTS_SUPPORTED
/////////////////////////////////////////////////////////////////////////////////////////
/**
 \brief  The function operates a SPI access without addressing.

        The first two bytes of an access to the EtherCAT ASIC always deliver the AL_Event register (0x220).
        It will be saved in the global "EscALEvent"
*////////////////////////////////////////////////////////////////////////////////////////
static void GetInterruptRegister(void)
{
	UINT8 dataSend[2];
#if (TARGET_MCU == APM32_MCU)
	
    #if AL_EVENT_ENABLED
    DISABLE_AL_EVENT_INT;
		#endif
	
    /* select the SPI */
    SELECT_SPI;
	

	#if SPI_OPTIMIZED
	dataSend[0]=(UINT8) (0xFF >> 5);
	dataSend[1]=(UINT8) (((0xFF & 0x1F) << 3) | ESC_RD);
	//RxTxSpiData_nR(&EscALEvent.Byte[0],2);
	//RxTxSpiData_nW(dataSend,2);
	RxTxSpiData_nRW(&EscALEvent.Byte[0],dataSend,2);
	#else
	EscALEvent.Byte[0] = RxTxSpiData((UINT8) (0xFF >> 5));
	EscALEvent.Byte[1] = RxTxSpiData((UINT8) (((0xFF & 0x1F) << 3) | ESC_RD));
	#endif
	/* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */
	
  DESELECT_SPI;
 #if AL_EVENT_ENABLED
    ENABLE_AL_EVENT_INT;
 #endif
#endif
}

/////////////////////////////////////////////////////////////////////////////////////////
/**
 \brief  The function operates a SPI access without addressing.
        Shall be implemented if interrupts are supported else this function is equal to "GetInterruptRegsiter()"

        The first two bytes of an access to the EtherCAT ASIC always deliver the AL_Event register (0x220).
        It will be saved in the global "EscALEvent"
*////////////////////////////////////////////////////////////////////////////////////////
static void ISR_GetInterruptRegister(void)
{
	UINT8 dataSend[2];
#if (TARGET_MCU == APM32_MCU)
	    /* SPI should be deactivated to interrupt a possible transmission */
    DESELECT_SPI;

    /* select the SPI */
    SELECT_SPI;

	#if SPI_OPTIMIZED
	dataSend[0]=(UINT8) (0xFF >> 5);
	dataSend[1]=(UINT8) (((0xFF & 0x1F) << 3) | ESC_RD);
	//RxTxSpiData_nR(&EscALEvent.Byte[0],2);
	//RxTxSpiData_nW(dataSend,2);
	RxTxSpiData_nRW(&EscALEvent.Byte[0],dataSend,2);
	#else
	EscALEvent.Byte[0] = RxTxSpiData(0x00);
	EscALEvent.Byte[1] = RxTxSpiData(0x00);
	#endif
  /* if the SPI transmission rate is higher than 15 MBaud, the Busy detection shall be
       done here */

    DESELECT_SPI;
#endif
}
/*ECATCHANGE_END(V5.12) EL9800 1*/

/*--------------------------------------------------------------------------------------
------
------    exported hardware access functions
------
--------------------------------------------------------------------------------------*/


/////////////////////////////////////////////////////////////////////////////////////////
/**
\return     0 if initialization was successful

 \brief    This function intialize the Process Data Interface (PDI) and the host controller.
*////////////////////////////////////////////////////////////////////////////////////////
UINT8 HW_Init(void)
{
    UINT32 intMask;

#if (TARGET_MCU == APM32_MCU)
	SetSpiDR2DMA();
	SPI_Enable(hspix);
#endif

    do
    {
        intMask = 0x93;
        HW_EscWriteDWord(intMask, ESC_AL_EVENTMASK_OFFSET);
        intMask = 0;
        HW_EscReadDWord(intMask, ESC_AL_EVENTMASK_OFFSET);
    } while (intMask != 0x93);

    intMask = 0x00;

    HW_EscWriteDWord(intMask, ESC_AL_EVENTMASK_OFFSET);

#if (TARGET_MCU == APM32_MCU)
		
    INIT_ESC_INT
    ENABLE_ESC_INT();

    INIT_SYNC0_INT
    INIT_SYNC1_INT

    ENABLE_SYNC0_INT;
    ENABLE_SYNC1_INT;

    INIT_ECAT_TIMER;
    START_ECAT_TIMER;

    /* enable all interrupts */
    ENABLE_GLOBAL_INT;
#endif
    return 0;
}


/////////////////////////////////////////////////////////////////////////////////////////
/**
 \brief    This function shall be implemented if hardware resources need to be release
        when the sample application stops
*////////////////////////////////////////////////////////////////////////////////////////
void HW_Release(void)
{
}

/////////////////////////////////////////////////////////////////////////////////////////
/**
 \return    first two Bytes of ALEvent register (0x220)

 \brief  This function gets the current content of ALEvent register
*////////////////////////////////////////////////////////////////////////////////////////
UINT16 HW_GetALEventRegister(void)
{
    GetInterruptRegister();
    return EscALEvent.Word;
}

/////////////////////////////////////////////////////////////////////////////////////////
/**
 \return    first two Bytes of ALEvent register (0x220)

 \brief  The SPI PDI requires an extra ESC read access functions from interrupts service routines.
        The behaviour is equal to "HW_GetALEventRegister()"
*////////////////////////////////////////////////////////////////////////////////////////
UINT16 HW_GetALEventRegister_Isr(void)
{
     ISR_GetInterruptRegister();
    return EscALEvent.Word;
}



/*ECATCHANGE_START(V5.12) EL9800 1*/
/////////////////////////////////////////////////////////////////////////////////////////
/**
 \param pData        Pointer to a byte array which holds data to write or saves read data.
 \param Address     EtherCAT ASIC address ( upper limit is 0x1FFF )    for access.
 \param Len            Access size in Bytes.

 \brief  This function operates the SPI read access to the EtherCAT ASIC.
*////////////////////////////////////////////////////////////////////////////////////////
void HW_EscRead( MEM_ADDR *pData, UINT16 Address, UINT16 Len )
{
#if (TARGET_MCU == APM32_MCU)
	#if SPI_OPTIMIZED
    /* HBu 24.01.06: if the SPI will be read by an interrupt routine too the
                     mailbox reading may be interrupted but an interrupted
                     reading will remain in a SPI transmission fault that will
                     reset the internal Sync Manager status. Therefore the reading
                     will be divided in 1-byte reads with disabled interrupt */
    UINT16 i = Len;
		UINT8	dataSend[4];
		UINT8 dataRead[4];
    UINT8 *pTmpData = (UINT8 *)pData;

    /* loop for all bytes to be read */
    while ( i-- > 0 )
    {
			#if AL_EVENT_ENABLED
        /* the reading of data from the ESC can be interrupted by the
           AL Event ISR, in that case the address has to be reinitialized,
           in that case the status flag will indicate an error because
           the reading operation was interrupted without setting the last
           sent byte to 0xFF */
        DISABLE_AL_EVENT_INT;
			#endif
			#if(ESC_TYPE == ESC_GT100)
        // AddressingEsc( Address, ESC_RD );
					AddressingEsc_Data(dataSend,Address, ESC_RD );
			
        /* when reading the last byte the DI pin shall be 1 */

				dataSend[3] = 0xFF;

			//RxTxSpiData_nR(pTmpData,1);
			//RxTxSpiData_nW(&dataSend,1);
			RxTxSpiData_nRW(dataRead,dataSend,4);
			*pTmpData++ = dataRead[3];
			#else //(ESC_TYPE == ESC_GT120)
			  AddressingEsc( Address, ESC_RD );
        /* when reading the last byte the DI pin shall be 1 */
				dataSend[0] = 0xFF;
				RxTxSpiData_nRW(dataRead,dataSend,1);
				*pTmpData++ = dataRead[0];
			#endif
        /* enable the ESC interrupt to get the AL Event ISR the chance to interrupt,
           if the next byte is the last the transmission shall not be interrupted,
           otherwise a sync manager could unlock the buffer, because the last was
           read internally */
   #if AL_EVENT_ENABLED
        ENABLE_AL_EVENT_INT;
#endif
        /* there has to be at least 15 ns + CLK/2 after the transmission is finished
           before the SPI1_SEL signal shall be 1 */
        DESELECT_SPI;
        /* next address */
        Address++;
    }	
		#else
		    /* HBu 24.01.06: if the SPI will be read by an interrupt routine too the
                     mailbox reading may be interrupted but an interrupted
                     reading will remain in a SPI transmission fault that will
                     reset the internal Sync Manager status. Therefore the reading
                     will be divided in 1-byte reads with disabled interrupt */
    UINT16 i = Len;
    UINT8 *pTmpData = (UINT8 *)pData;

    /* loop for all bytes to be read */
    while ( i-- > 0 )
    {
			#if AL_EVENT_ENABLED
        /* the reading of data from the ESC can be interrupted by the
           AL Event ISR, in that case the address has to be reinitialized,
           in that case the status flag will indicate an error because
           the reading operation was interrupted without setting the last
           sent byte to 0xFF */
        DISABLE_AL_EVENT_INT;
#endif
         AddressingEsc( Address, ESC_RD );

        /* when reading the last byte the DI pin shall be 1 */
			*pTmpData++= RxTxSpiData(0xFF);
        /* enable the ESC interrupt to get the AL Event ISR the chance to interrupt,
           if the next byte is the last the transmission shall not be interrupted,
           otherwise a sync manager could unlock the buffer, because the last was
           read internally */
   #if AL_EVENT_ENABLED
        ENABLE_AL_EVENT_INT;
#endif
        /* there has to be at least 15 ns + CLK/2 after the transmission is finished
           before the SPI1_SEL signal shall be 1 */
        DESELECT_SPI;
        /* next address */
        Address++;
    }	
		#endif
#endif
}

/////////////////////////////////////////////////////////////////////////////////////////
/**
 \param pData        Pointer to a byte array which holds data to write or saves read data.
 \param Address     EtherCAT ASIC address ( upper limit is 0x1FFF )    for access.
 \param Len            Access size in Bytes.

\brief  The SPI PDI requires an extra ESC read access functions from interrupts service routines.
        The behaviour is equal to "HW_EscRead()"
*////////////////////////////////////////////////////////////////////////////////////////
void HW_EscReadIsr( MEM_ADDR *pData, UINT16 Address, UINT16 Len )
{
#if (TARGET_MCU == APM32_MCU)
	#if SPI_OPTIMIZED	
    UINT16 i = Len;
		//UINT8 dataSend[30];
   UINT8 *pTmpData = (UINT8 *)pData;
	#if 1
    /* send the address and command to the ESC */
     ISR_AddressingEsc( Address, ESC_RD02 );
    /* loop for all bytes to be read */
	
    /* when reading the last byte the DI pin shall be 1 */
		//memset(SpiDummyBuff,0x00,i);//zzc cost too muchu time
		SpiDummyBuff[i-1] = 0xFF;
		RxTxSpiData_nRW(pTmpData,SpiDummyBuff,i);
		SpiDummyBuff[i-1] = 0x00;
		
    /* there has to be at least 15 ns + CLK/2 after the transmission is finished
       before the SPI1_SEL signal shall be 1 */
    DESELECT_SPI;	
	#else //cost too muchu time
    /* send the address and command to the ESC */
     //ISR_AddressingEsc( Address, ESC_RD );
			memset(SpiTxBuff,0x00,i+4); 
     ISR_AddressingEsc_Data(SpiTxBuff,Address, ESC_RD );
    /* loop for all bytes to be read */
	
		//RxTxSpiData_nR(pTmpData,i);
	
	
		if(Address<0x2000)
		{
    /* when reading the last byte the DI pin shall be 1 */
			SpiTxBuff[i+2]=0xFF;
			RxTxSpiData_nRW(pTmpData,SpiTxBuff,i+3);
			memcpy(pTmpData,&SpiRxBuff[3],i);//zzc cost too muchu time
		}
		else
		{
    /* when reading the last byte the DI pin shall be 1 */
			SpiTxBuff[i+3]=0xFF;
			RxTxSpiData_nRW(pTmpData,SpiTxBuff,i+4);
			memcpy(pTmpData,&SpiRxBuff[4],i);//zzc cost too muchu time
		}
	#endif
			
    /* there has to be at least 15 ns + CLK/2 after the transmission is finished
       before the SPI1_SEL signal shall be 1 */
    DESELECT_SPI;	
	#else
    UINT16 i = Len;
    UINT8 data = 0;

   UINT8 *pTmpData = (UINT8 *)pData;

    /* send the address and command to the ESC */
     ISR_AddressingEsc( Address, ESC_RD );
    /* loop for all bytes to be read */
    while ( i-- > 0 )
    {
        if ( i == 0 )
        {
            /* when reading the last byte the DI pin shall be 1 */
            data = 0xFF;
        }

			*pTmpData++= RxTxSpiData(data);
    }

    /* there has to be at least 15 ns + CLK/2 after the transmission is finished
       before the SPI1_SEL signal shall be 1 */
    DESELECT_SPI;	
		#endif
#endif
}

/////////////////////////////////////////////////////////////////////////////////////////
/**
 \param pData        Pointer to a byte array which holds data to write or saves write data.
 \param Address     EtherCAT ASIC address ( upper limit is 0x1FFF )    for access.
 \param Len            Access size in Bytes.

  \brief  This function operates the SPI write access to the EtherCAT ASIC.
*////////////////////////////////////////////////////////////////////////////////////////
void HW_EscWrite( MEM_ADDR *pData, UINT16 Address, UINT16 Len )
{
#if (TARGET_MCU == APM32_MCU)
	#if SPI_OPTIMIZED
    UINT16 i = Len;
    VARVOLATILE UINT8 dummy;
		UINT8 dataRead[4];
		UINT8 dataSend[4];
    UINT8 *pTmpData = (UINT8 *)pData;

    /* loop for all bytes to be written */
    while ( i-- > 0 )
    {
   #if AL_EVENT_ENABLED
        /* the reading of data from the ESC can be interrupted by the
           AL Event ISR, so every byte will be written separate */
        DISABLE_AL_EVENT_INT;
	 #endif
        /* HBu 24.01.06: wrong parameter ESC_RD */
         //AddressingEsc( Address, ESC_WR );
         AddressingEsc_Data(dataSend, Address, ESC_WR );

        /* enable the ESC interrupt to get the AL Event ISR the chance to interrupt */
        /* SPI1_BUF must be read, otherwise the module will not transfer the next received data from SPIxSR to SPIxRXB.*/
			
				//RxTxSpiData_nR(dataRX,1);
				//RxTxSpiData_nW(pTmpData,1);
				dataSend[2]=*pTmpData;
				RxTxSpiData_nRW(dataRead,dataSend,3);
				pTmpData++;
			
    #if AL_EVENT_ENABLED
        ENABLE_AL_EVENT_INT;
		#endif
        DESELECT_SPI;
        /* next address */
        Address++;
    }
	#else
    UINT16 i = Len;
    VARVOLATILE UINT8 dummy;

    UINT8 *pTmpData = (UINT8 *)pData;

    /* loop for all bytes to be written */
    while ( i-- > 0 )
    {
   #if AL_EVENT_ENABLED
        /* the reading of data from the ESC can be interrupted by the
           AL Event ISR, so every byte will be written separate */
        DISABLE_AL_EVENT_INT;
	 #endif
        /* HBu 24.01.06: wrong parameter ESC_RD */
         AddressingEsc( Address, ESC_WR );

        /* enable the ESC interrupt to get the AL Event ISR the chance to interrupt */
        /* SPI1_BUF must be read, otherwise the module will not transfer the next received data from SPIxSR to SPIxRXB.*/
				dummy= RxTxSpiData(*pTmpData++);
			
    #if AL_EVENT_ENABLED
        ENABLE_AL_EVENT_INT;
		#endif

        DESELECT_SPI;
        /* next address */
        Address++;
    }
		#endif
#endif
}


/////////////////////////////////////////////////////////////////////////////////////////
/**
 \param pData        Pointer to a byte array which holds data to write or saves write data.
 \param Address     EtherCAT ASIC address ( upper limit is 0x1FFF )    for access.
 \param Len            Access size in Bytes.

 \brief  The SPI PDI requires an extra ESC write access functions from interrupts service routines.
        The behaviour is equal to "HW_EscWrite()"
*////////////////////////////////////////////////////////////////////////////////////////
void HW_EscWriteIsr( MEM_ADDR *pData, UINT16 Address, UINT16 Len )
{
#if (TARGET_MCU == APM32_MCU)
	#if SPI_OPTIMIZED
		#if 1
    UINT16 i = Len;
		//UINT8 dataRX[30];
    VARVOLATILE UINT16 dummy;
    UINT8 *pTmpData = (UINT8 *)pData;

    /* send the address and command to the ESC */
     ISR_AddressingEsc( Address, ESC_WR );
    /* loop for all bytes to be written */

				//RxTxSpiData_nR(dataRX,i);
				//RxTxSpiData_nW(pTmpData,i);
				RxTxSpiData_nRW(SpiDummyBuff,pTmpData,i);

    /* there has to be at least 15 ns + CLK/2 after the transmission is finished
       before the SPI1_SEL signal shall be 1 */
    DESELECT_SPI;	
		#else
    UINT16 i = Len;
		UINT8 dataRX[30];
    VARVOLATILE UINT16 dummy;
    UINT8 *pTmpData = (UINT8 *)pData;

    /* send the address and command to the ESC */
     //ISR_AddressingEsc( Address, ESC_WR );
     ISR_AddressingEsc_Data(SpiTxBuff, Address, ESC_WR );
    /* loop for all bytes to be written */

				//RxTxSpiData_nR(dataRX,i);
				//RxTxSpiData_nW(pTmpData,i);
			if(Address<0x2000)
			{
				memcpy(&SpiTxBuff[2],pTmpData,i);//zzc cost too muchu time
				RxTxSpiData_nRW(SpiRxBuff,SpiTxBuff,i+2);
			}
			else
			{
				memcpy(&SpiTxBuff[3],pTmpData,i);//zzc cost too muchu time
				RxTxSpiData_nRW(SpiRxBuff,SpiTxBuff,i+3);
			}

    /* there has to be at least 15 ns + CLK/2 after the transmission is finished
       before the SPI1_SEL signal shall be 1 */
    DESELECT_SPI;
		
		#endif
	#else
    UINT16 i = Len;
    VARVOLATILE UINT16 dummy;
    UINT8 *pTmpData = (UINT8 *)pData;

    /* send the address and command to the ESC */
     ISR_AddressingEsc( Address, ESC_WR );
    /* loop for all bytes to be written */
    while ( i-- > 0 )
    {
        /* start transmission */
				dummy= RxTxSpiData(*pTmpData);
      /* increment data pointer */
        pTmpData++;
    }

    /* there has to be at least 15 ns + CLK/2 after the transmission is finished
       before the SPI1_SEL signal shall be 1 */
    DESELECT_SPI;
	#endif
#endif
}
/*ECATCHANGE_END(V5.12) EL9800 1*/

#if (TARGET_MCU == APM32_MCU)
void EINT1_IRQHandler(void)
{
		PDI_Isr();
		EINT_ClearIntFlag(EINT_LINE_1);
}
void EINT2_IRQHandler(void)
{
		Sync1_Isr();
		EINT_ClearIntFlag(EINT_LINE_2);
}
void EINT3_IRQHandler(void)
{
		Sync0_Isr();
		EINT_ClearIntFlag(EINT_LINE_3);
}
void TMR2_IRQHandler(void)
{
		ECAT_CheckTimer();
		TMR_ClearIntFlag(TMR2, TMR_INT_UPDATE);
}


#endif

#endif
/** @} */
